<?php

namespace app\service;


use app\exception\ModelEmptyException;
use app\exception\ModelException;
use app\exception\ModelNotUniqueException;
use app\model\BigField;
use app\model\ThemeFile;
use app\model\Theme;
use think\facade\Db;
use think\response\Json;

class ThemeService
{
    public $themePath;
    public $rootPath;
    public $websitePath;
    public $originalPath;

    public $siteId;
    private $Theme;
    private $ThemeFile;
    private $adminViewPath;

    public function __construct()
    {
        $this->Theme = new Theme();
        $this->ThemeFile = new ThemeFile();
        $this->themePath = config('view.view_dir_name');
        $this->rootPath = app()->getRootPath();
        $this->websitePath = $this->rootPath . 'public/themes/website/';
        $this->originalPath = $this->rootPath . 'public/themes/hc_original/';
        $this->adminViewPath = $this->rootPath . 'view/website/';
    }

    /**
     * 卸载模版
     * @throws ModelException
     * @throws ModelEmptyException
     */
    public function uninstallTheme($themeId, $siteId, $sellerId): Json
    {
        Db::startTrans();
        try {
            $theme = $this->getThemeModel($themeId, $siteId, $sellerId);
            $this->updateThemeDelete($theme, $siteId , $sellerId);
            Db::commit();
        } catch (\Exception | ModelException $e) {
            Db::rollback();
            return jsonReturn(-6, $e->getMessage());
        }
        return jsonReturn(0, '卸载成功');
    }

    /**
     * @throws ModelException
     */
    public function updateThemeDelete($theme, $siteId, $sellerId,$isDel = true)
    {
        // 文件真实删除
        $this->deleteFile($this->websitePath . $siteId . DIRECTORY_SEPARATOR . $theme['lang'] . DIRECTORY_SEPARATOR . $theme['name']);

        if($isDel){
            $this->deleteFile($this->adminViewPath . $siteId . DIRECTORY_SEPARATOR . $theme['lang'] . DIRECTORY_SEPARATOR . $theme['name']);
        }
        // 文件数据库删除
        $this->ThemeFile->delThemeFile(['theme_id' => $theme['id'], 'seller_id' => $sellerId, 'website_id' => $siteId]);
        $theme->delete();
    }

    /**
     * 模版安装
     * @throws ModelException
     */
    public function installTheme($theme, $siteId, $sellerId,$suffix = 'html',$lang='zh'): Json
    {
        // 系统模版根目录
        if(!$this->hasPath($this->websitePath)){
            mkdir($this->websitePath);
        }
        // 站点目录
        $tmpPath = $this->websitePath . $siteId;
        if(!$this->hasPath($tmpPath)){
            mkdir($tmpPath);
        }
        // 站点语言目录
        $tmpLangPath = $tmpPath . DIRECTORY_SEPARATOR . $lang;
        if(!$this->hasPath($tmpLangPath)){
            mkdir($tmpLangPath);
        }
        // 站点语言模版目录
        $tmpThemePath = $tmpPath . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $theme;
        if(!$this->hasPath($tmpThemePath)){
            mkdir($tmpThemePath);
        }else{
            if(!$this->uniqueTheme($theme, $siteId)){
                return jsonReturn(-4, '相同模版已经安装,请修改模版名称');
            }
        }
        $tmpAdminPath = $this->adminViewPath . $siteId;
        $tmpAdminLangPath = $tmpAdminPath . DIRECTORY_SEPARATOR . $lang;
        $tmpAdminThemePath = $tmpAdminPath . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR . $theme;
        if(!$this->hasPath($tmpAdminPath)){
            mkdir($tmpAdminPath);
        }
        if(!$this->hasPath($tmpAdminLangPath)){
            mkdir($tmpAdminLangPath);
        }
        if(!$this->hasPath($tmpAdminThemePath)){
            mkdir($tmpAdminThemePath);
        }
        $dir = $this->originalPath . $theme;

        return $this->installThemeFile($theme,$dir,$tmpThemePath,$tmpAdminThemePath,$siteId,$sellerId,$suffix,$lang);
    }

    /**
     * 模版更新
     * @throws ModelException
     * @throws ModelEmptyException
     */
    public function updateTheme($themeId, $siteId, $sellerId, $suffix = 'html'): Json
    {
        $theme = $this->getThemeModel($themeId,$siteId,$sellerId);
        $dir = $this->websitePath . $siteId . DIRECTORY_SEPARATOR . $theme['lang'] . DIRECTORY_SEPARATOR . $theme['name'];
        $manifest = "$dir/manifest.json";
        if (hcFileExist($manifest)) {
            // 文件真实删除
            $this->deleteFile($dir);

            $this->updateInstall($theme,$siteId, $sellerId, $suffix);

            return jsonReturn(0, '模版更新成功');
        } else {
            return jsonReturn(-5, '模板不存在');
        }
    }

    /**
     * 模版文件更新时安装
     */
    public function updateInstall($theme,$siteId, $sellerId, $suffix): Json
    {
        $tmpPath = $this->websitePath . $siteId . DIRECTORY_SEPARATOR . $theme['lang'] . DIRECTORY_SEPARATOR . $theme['name'] ;

        $tmpAdminPath = $this->adminViewPath . $siteId . DIRECTORY_SEPARATOR . $theme['lang'] . DIRECTORY_SEPARATOR . $theme['name'];

        return $this->installThemeFile($theme,$tmpAdminPath,$tmpPath,$tmpAdminPath,$siteId,$sellerId,$suffix,$theme['lang'],false);
    }

    /**
     * 安装模版文件公共方法
     * @param $theme
     * @param $dir
     * @param $tmpThemePath
     * @param $tmpAdminThemePath
     * @param $siteId
     * @param $sellerId
     * @param $suffix
     * @param $lang
     * @param bool $isInstall
     * @return Json
     */
    private function installThemeFile($theme, $dir, $tmpThemePath, $tmpAdminThemePath, $siteId, $sellerId, $suffix, $lang, bool $isInstall=true): Json
    {
        $manifest = "$dir/manifest.json";
        if (hcFileExist($manifest)) {
            $manifest = file_get_contents($manifest);
            $themeData = json_decode($manifest, true);
            Db::startTrans();
            try {
                if($isInstall){
                    $themeData['theme'] = $theme;
                    $themeData['lang'] = $lang;
                    $themeData['seller_id'] = $sellerId;
                    $themeData['website_id'] = $siteId;
                    $themeData['sort'] = empty($themeData['sort']) ? 1 : (int)$themeData['sort'];
                    $themeData['is_active'] = empty($theme['is_active']) ? 2 : $theme['is_active'];
                    $theme = $this->Theme->addTheme($themeData)['data'];
                    $this->copyFile($dir,$tmpAdminThemePath);
                    $dir = $tmpAdminThemePath;
                }
                $res = $this->copyFile($dir,$tmpThemePath);
                $this->updateThemeFiles($dir,$theme, $siteId, $sellerId, $suffix,$lang);
                if(!$res){
                    throw new \Exception('新模板安装失败');
                }
                // 没有激活模版，激活当前安装的模版
                $activeTheme = $this->Theme->getActiveTheme(['seller_id'=>$sellerId,'website_id'=>$siteId,'lang'=>$lang,'is_active'=>1])['data'];
                if(empty($activeTheme)){
                    $theme->is_active = 1;
                    $theme->save();
                }
                Db::commit();
            } catch (\Exception $e) {
                Db::rollback();
                if (hcFileExist($manifest)) {
                    $this->deleteFile($tmpThemePath);
                    if($isInstall){
                        $this->deleteFile($tmpAdminThemePath);
                    }
                }
                return jsonReturn(-8, $e->getMessage());
            }
            return jsonReturn(0, '模版安装成功');
        } else {
            return jsonReturn(-3, '模板不存在');
        }

    }

    /**
     * 更新模版存储数据
     * @param $dir
     * @param int $siteId
     * @param string $suffix
     * @throws ModelException
     */
    private function updateThemeFiles($dir, $theme,int $siteId, $sellerId, string $suffix = 'html',$lang = 'zh')
    {
        $themeDir = $dir;
        $tplFiles = $this->getDirTmp($dir, $suffix);
        $subDirs = scanSubDir($dir);
        foreach ($subDirs as $dir) {
            $subDirTplFileArr = $this->getDirTmp($dir, $suffix);
            $tplFiles = array_merge($tplFiles, $subDirTplFileArr);
        }
        // 对比数据库的保存的模版文件和更新的模版文件
        $themeFile = new ThemeFile();
        $dbTmpFile =  $themeFile->getAllCustomData(['website_id'=>$siteId,'seller_id'=>$sellerId,'lang'=>$lang,'theme_id'=>$theme['id']])['data'];
        if(!empty($dbTmpFile)){
            $dbTmpFile = array_column($dbTmpFile,'file');
            $tmpTplFiles = [];
            foreach ($tplFiles as $val){
                if(!empty($val)){
                    array_push($tmpTplFiles,basename($val));
                }
            }
            $delTpl = array_diff($dbTmpFile,$tmpTplFiles);
            if(!empty($delTpl)){
                $delWhere = [
                    ['website_id','=',$siteId],
                    ['seller_id','=',$sellerId],
                    ['lang','=',$lang],
                    ['theme_id','=',$theme['id']],
                    ['file','in',$delTpl],
                ];
                $themeFile -> delThemeFile($delWhere);
            }
        }

        foreach ($tplFiles as $tplFile) {
            $configFile = $tplFile . ".json";
            $tmpFilePath =  $tplFile.'.'.$suffix;
            $file = str_replace($themeDir . '/', '', $tplFile);
            $file = strtolower($file);
            $config = json_decode(file_get_contents($configFile), true);
            $findFile = $this->ThemeFile->existThemeFile(['theme_id' => $theme['id'], 'seller_id' => $sellerId, 'website_id' => $siteId,'lang'=>$lang, 'file' => $file])['data'];
            $isPublic = empty($config['is_public']) ? 2 : 1;
            $listOrder = empty($config['order']) ? 10001 : intval($config['order']);
            $configMore = empty($config['more']) ? [] : $config['more'];
            $more = $configMore;
            $BigField = new BigField();
            $content = htmlspecialchars_decode(file_get_contents($tmpFilePath));
            $editMore = json_encode($more);
            $this->setRealContent($content,$this->adminViewPath.$siteId . DIRECTORY_SEPARATOR,$this->websitePath.$siteId . DIRECTORY_SEPARATOR,$tmpFilePath);
            if (empty($findFile)) {
                $tmpFile = $this->ThemeFile->addThemeFile([
                    'theme_id' => $theme['id'],
                    'lang' => $lang,
                    'website_id' => $siteId,
                    'seller_id' => $sellerId,
                    'theme_name' => $theme['theme'],
                    'action' => $config['action'],
                    'file' => $file,
                    'name' => $config['name'],
                    'description' => $config['description'],
                    'is_public' => $isPublic,
                    'sort' => $listOrder
                ])['data'];
                $BigField->addBigField([
                    'seller_id' => $sellerId,
                    'theme_file_id' => $tmpFile->id,
                    'content' => $content,
                    'draft_content' => $content,
                    'more' => $editMore,
                    'draft_more' => $editMore,
                ]);
            } else { // 更新文件
                $this->ThemeFile->updateThemeFile(['theme_id' => $theme['id'], 'seller_id' => $sellerId, 'website_id' => $siteId, 'id' => $findFile['id']], [
                    'theme_name' => $theme['theme'],
                    'action' => $config['action'],
                    'file' => $file,
                    'name' => $config['name'],
                    'description' => $config['description'],
                    'is_public' => $isPublic,
                    'sort' => $listOrder,
                ]);
                $BigField->updateBigField(['seller_id'=>$sellerId,'theme_file_id'=>$findFile['id']],[
                    'seller_id' => $sellerId,
                    'theme_file_id' => $findFile->id,
                    'content' => $content,
                    'draft_content' => $content,
                    'more' => $editMore,
                    'draft_more' => $editMore,
                ]);
            }
        }

//        // 检查安装过的模板文件是否已经删除
//        $files = $this->ThemeFile->getAllThemeFile(['theme_id' => $theme['id'], 'website_id' => $siteId, 'seller_id' => $sellerId])['data'];
//
//        foreach ($files as $themeFile) {
//            $tplFile = $themeDir . '/' . $themeFile['file'] . '.' . $suffix;
//            $tplFileConfigFile = $themeDir . '/' . $themeFile['file'] . '.json';
//            if (!is_file($tplFile) || !hcFileExist($tplFileConfigFile)) {
//                $this->ThemeFile->delThemeFile(['theme_id' => $theme['id'], 'file' => $themeFile['file']]);
//            }
//        }
    }

    /**
     * 获取需要操作的模板模型
     * @throws ModelException
     * @throws ModelEmptyException
     */
    public function getThemeModel($themeId, $siteId, $sellerId)
    {
        return $this->Theme->getTheme(['id'=>$themeId,'seller_id' => $sellerId,'website_id' => $siteId])['data'];
    }

    public function hasPath($path): bool
    {
        if (is_dir($path)) {
            return true;
        }
        return false;
    }

    /**
     * @throws ModelException
     */
    public function uniqueTheme($theme, $websiteId): bool
    {
        try {
            $this->Theme->saveUnique(['theme'=>$theme,'website_id'=>$websiteId]);
        }catch (ModelNotUniqueException $e){
            return false;
        }
        $dir = $this->websitePath . $websiteId;
        $files = hcScanDir("$dir/*");
        if(empty($files)){
            return true;
        }
        foreach($files as $file){
            $manifest = $dir.'/' . $file . '/manifest.json';
            if(hcFileExist($manifest)){
                $config = file_get_contents($manifest);
                $config = json_decode($config,true);
                if($theme === $config['name']){
                    return false;
                    break;
                }
            }
        }
        return true;
    }

    /**
     * 获取目录下模版文件
     * @param $dir
     * @param string $suffix
     */
    public function getDirTmp($dir, string $suffix = 'html'): array
    {
        $tmpFiles = [];
        $subDirTplFiles = hcScanDir("$dir/*.$suffix");

        foreach ($subDirTplFiles as $tmpFile) {
            $tmpFile = "$dir/$tmpFile";
            $configFile = preg_replace("/\.$suffix$/", '.json', $tmpFile);
            $tplFileNoSuffix = preg_replace("/\.$suffix$/", '', $tmpFile);
            if (is_file($tmpFile) && hcFileExist($configFile)) {
                array_push($tmpFiles, $tplFileNoSuffix);
            }
        }
        return $tmpFiles;
    }

    /**
     * 文件夹列表
     * @throws ModelEmptyException
     * @throws ModelException
     */
    public function filesPath($siteId,$lang='zh'): Json
    {
        $pathName = $this->adminViewPath . $siteId . DIRECTORY_SEPARATOR .$lang;
        $res = $this->recurDir($pathName,$siteId,$pathName);
        return jsonReturn(0, 'success', $res);
    }

    /**
     * 文件列表
     * @param $path
     * @param int $siteId
     * @return Json
     */
    public function files($path,int $siteId,$lang='zh'): Json
    {
        $pathName = $this->adminViewPath .$siteId.DIRECTORY_SEPARATOR . $lang .$path;
        $this->siteId = $siteId;
        $files = $this->getFiles($pathName,$this->adminViewPath .$siteId);
        return jsonReturn(0, 'success', $files);
    }

    public function getFiles($pathName,$tmpPath): array
    {
        $files = [];
        if (!is_dir($pathName) || !is_readable($pathName)) {
            return $files;
        }
        $res = scandir($pathName);
        foreach($res as $file){
            $tmp = [];
            if (in_array($file, array('.', '..'))) {
                continue;
            }
            $fullPath = $pathName .'/'.$file;

            if(is_dir($fullPath)){
                $tmp = [
                    'type'  => 'path',
                ];
            }else{
                $tmp = [
                    'type'  => fileFormat($file),
                ];
            }
            $tmp['path'] = str_replace($tmpPath,'',$fullPath);
            $tmp['title'] = $file;
            array_push($files,$tmp);
        }
        return $files;
    }

    /**
     * 获取树形目录结构
     *
     * @param $pathName
     * @return array
     */
    public function recurDir($pathName,$siteId,$tmpPath): array
    {
        //将结果保存在result变量中
        $result = array();
        $temp = array();
        //判断传入的变量是否是目录
        if (!is_dir($pathName) || !is_readable($pathName)) {
            return $result;
        }
        //取出目录中的文件和子目录名,使用scandir函数
        $allFiles = scandir($pathName);
        //遍历他们
        $i = 0;
        foreach ($allFiles as $fileName) {
            //判断是否是.和..因为这两个东西神马也不是。。。
            if (in_array($fileName, array('.', '..'))) {
                continue;
            }
            //路径加文件名
            $fullName = $pathName . '/' . $fileName;
            //如果是目录的话就继续遍历这个目录
            if (is_dir($fullName)) {
                //将这个目录中的文件信息存入到数组中
                $result[$i] = [
                    'title' => $fileName,
                    'path'  => str_replace($tmpPath,'',$fullName),
                    'child' => $this->recurDir($fullName,$siteId,$tmpPath)
                ];
                $i++;
            }

        }
        return $result;
    }

    /**
     * 文件复制
     * @param $source
     * @param $destination
     * @param bool $child
     * @return bool
     */
    public function copyFile($source, $destination, bool $child = true): bool
    {
        if (!is_dir($source)) {
            return false;
        }
        if(!$this->hasPath($destination)){
            mkdir($destination, 0755);
        }

        $handle = dir($source);

        while($entry = $handle->read()){
            if($entry != '.' && $entry != '..') {
                if (is_dir($source.'/'.$entry)) {
                    if ($child) {
                        $this->copyFile($source . "/" . $entry, $destination . "/" . $entry, $child);
                    }
                } else {
                    copy($source . "/" . $entry, $destination . "/" . $entry);
                }
            }
        }
        return true;
    }

    /**
     * 文件删除
     * @param $path
     * @return bool
     */
    public function deleteFile($path): bool
    {
        if(is_dir($path) && $this->hasPath($path)){
            //扫描一个文件夹内的所有文件夹和文件并返回数组
            $p = scandir($path);
            //如果 $p 中有两个以上的元素则说明当前 $path 不为空
            if(count($p)>2){
                foreach($p as $val){
                    //排除目录中的.和..
                    if($val !="." && $val !=".."){
                        //如果是目录则递归子目录，继续操作
                        if(is_dir($path.'/'.$val)){
                            //子目录中操作删除文件夹和文件
                            $this->deleteFile($path.'/'.$val.'/');
                        }else{
                            //如果是文件直接删除
                            unlink($path.'/'.$val);
                        }
                    }
                }
            }
        }
        if($this->hasPath($path)){
            //删除目录
            return rmdir($path);
        }else{
            return true;
        }
    }

    /**
     * 格式化后台模版文件给前台
     * @param $html
     * @return array|string|string[]|null
     */
    public function pureBackendEditCode(&$html)
    {
        $pattern = "/([.\\n])?data-block-id=([\w\W]*?)data-hc-block-control=\"true\"([.\\n])?/";
        $html = preg_replace($pattern,'',$html);
        $pattern1 = '/\{include file=\"public\/backend_edit\" \}/';
        $html =  preg_replace($pattern1,'',$html);
        return $html;
    }

    /**
     * 设置前排模版内容
     * @param $content
     * @param $originalPath
     * @param $realpath
     * @param $tmpFilePath
     */
    public function setRealContent($content,$originalPath,$realpath,$tmpFilePath)
    {
        $realPath = str_replace($originalPath,$realpath,$tmpFilePath);

        $realContent = $this->pureBackendEditCode($content);

        file_put_contents($realPath,$realContent);
    }

    public function isPath($pathName): bool
    {
        //判断传入的变量是否是目录
        if (!is_dir($pathName) || !is_readable($pathName)) {
            return false;
        }
        return true;
    }
}